package day15;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

//(面向对象+集合+IO流+线程+线程池+Socket)
// 模拟用户登录注册聊天功能(私聊)
//客户端   用户系统
public class AccountSystem {
    static Scanner sc = new Scanner(System.in);
    static List<Account> list = new ArrayList<>();
    //可周期线程池
    static ScheduledExecutorService ses =
            Executors.newScheduledThreadPool(10);

    public static void main(String[] args) {
        one();
    }

    public static void one() {
        while (true) {
            System.out.println("======用户私聊系统======");
            System.out.println("1.登录");
            System.out.println("2.注册");
            System.out.println("3.退出");
            System.out.println("请输入编号");
            int n = sc.nextInt();
            switch (n) {
                case 1:
                    login();
                    break;
                case 2:
                    register();
                    break;
                case 3:
                    return;
            }
        }
    }

    //如果第一次登录成功  可以记录缓存(登录成功的记录)
    //如果下一次再登录  还是相同的账号 就不需要输入密码 直接免登录
    //缓存信息 不能一直存在  30秒之后失效
    public static void login() {
        System.out.println("请输入账号");
        String account = sc.next();
        //验证是否登录过  two();
        Account acc = checkCookie(account);
        if (acc != null) {
            two(acc);
            return;
        }
        System.out.println("请输入密码");
        String password = sc.next();
        for (Account a : list) {
            if (account.equals(a.username) && password.equals(a.password)) {
                System.out.println("是否保存本地缓存y/n,30秒内免登录");
                String yn = sc.next();
                if ("y".equals(yn)) {
                    addCookie(a);
                    //创建可周期定长线程池    30S首次延迟功能
                    ses.schedule(() -> {
                        removeCookie(account);
                    }, 30, TimeUnit.SECONDS);
                }

                two(a);
                return;
            }
        }
    }

    public static void removeCookie(String account) {
        //删除缓存
        File f = new File("d://a//cookie-" + account + ".txt");
        if (f.delete()) {
            System.out.println("缓存失效");
        }
    }

    private static Account checkCookie(String account) { //验证是否登录
        try (BufferedReader br =
                     new BufferedReader(
                             new InputStreamReader(
                                     new FileInputStream("d://a//cookie-" + account + ".txt")))) {
            String msg = br.readLine();
            //将账号加密后和之前保存的加密信息  比较是否一样
            if (msg != null && msg.equals(md5(account))) {
                for (Account a : list) {
                    if (a.username.equals(account)) return a;
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("首次登录");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void addCookie(Account a) { //本地缓存
        try (FileOutputStream fos = new FileOutputStream("d://a//cookie-" + a.username + ".txt")) {
            fos.write(md5((a.username)).getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void register() {
        System.out.println("请输入账号");
        String account = sc.next();
        System.out.println("请输入密码");
        String password = sc.next();
        Account a = new Account(account, password);
        //存储集合
        list.add(a);
        //做序列化

    }

    //登录成功进入二级菜单    连接socket服务端
    public static void two(Account a) {
        System.out.println(a.username + "登录成功");
        Socket client = null;
        try {
            client = new Socket("127.0.0.1", 8888);
        } catch (IOException e) {

        }
        //存储客户端对象
        //AccountServer.map.put(a.username, client);

        //接收读取服务端的数据(怎么读不影响主程序  所以是客户端线程)
        ClientThread t = new ClientThread(client);
        t.start();
        while (true) {
            System.out.println("1.聊天功能");   //进行私聊
            System.out.println("2.退出登录");   //清除缓存
            System.out.println("0.返回上级");
            int n = sc.nextInt();
            switch (n) {
                case 1:
                    chat(a, client);
                    break;
                case 2:
                    removeCookie(a.username);
                    //客户端线程要停止  还要删除socket对象
                    t.b = true;
                    AccountServer.map.remove(a.username);
                    break;
                case 0:
                    return;
            }
        }
    }

    //还有告诉服务端 谁是发送端 谁是 接受端
    //进行私聊：创建输出流 写数据给发送方
    public static void chat(Account a, Socket client) {
        System.out.println("请输入对方的账号");
        String to = sc.next();
        try {
            OutputStream os = client.getOutputStream();
            //第一次给服务端写入时：告诉他发送方和接收方
            os.write((a.username + "=>" + to + "\n").getBytes());
            //开始聊天
            while (true) {
                System.out.println("请输入向" + to + "发送的消息,输入esc退出");
                String msg = sc.next();
                if ("esc".equals(msg)) {   //退出聊天(不发送消息,都是账号没有退出，不影响接收消息)
                    //os.write((a.username + "说:esc\n").getBytes());
                    break;
                }
                os.write((a.username + "说:" + msg + "\n").getBytes());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static String md5(String msg) {  //不可逆加密处理
        StringBuilder hexString = null;
        try {
            // 获取 MessageDigest 类的实例，指定算法为 MD5
            MessageDigest digest = MessageDigest.getInstance("MD5");
            // 对字符串进行加密
            byte[] encodedHash = digest.digest(msg.getBytes());
            // 将加密后的字节数组转换为十六进制字符串
            hexString = new StringBuilder();
            for (byte b : encodedHash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return hexString.toString();
    }

}

class Account {
    String username;
    String password;

    public Account(String username, String password) {
        this.username = username;
        this.password = password;
    }
}

//Socket服务端: 是和主程序独立的 而且是 提前开好的
class AccountServer {
    // key：账号   value：对应的客户端对象
    static Map<String, Socket> map = new HashMap<>();

    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8888);
        System.out.println("启动服务端成功!");
        while (true) {
            Socket client = server.accept();
            //线程
            new ServerThread(client).start();
        }
    }
}

class ServerThread extends Thread {
    Socket client;

    public ServerThread(Socket client) {
        this.client = client;
    }

    //逻辑：先接收数据(得到 发送方 和 接收方 的client)
    //读取发送方  再写入给接收方
    //如果不在线呢？ 可以实现离线消息存储(存入文件中) 也可以不发送消息
    public void run() {
        try (BufferedReader br = new BufferedReader(
                new InputStreamReader(client.getInputStream()))) {
            String formAndTo = br.readLine();   //a(发) => b(收)
            //System.out.println(formAndTo + "----------");
            //fromTo[0] 就是发送方   fromTo[1] 就是接收方
            String[] fromTo = formAndTo.split("=>");
            AccountServer.map.put(fromTo[0], client);
            while (true) {
                //开始读取数据
                String msg = br.readLine();
                /*if (msg.contains("esc")) {  //是否退出  目的是要结束线程 我不需要给别人发消息了
                    return;
                }*/
                if (AccountServer.map.containsKey(fromTo[1])) {  //对方在线
                    //给接收方直接写入数据
                    System.out.println("对方在线");
                    Socket toclient = AccountServer.map.get(fromTo[1]);
                    OutputStream os = toclient.getOutputStream();
                    os.write((msg + "\n").getBytes());
                } else { //对方不在线
                    System.out.println("不在线");
                    //给发送方返回一个结果
                    OutputStream os = client.getOutputStream();
                    os.write((fromTo[1] + "目前不在线\n").getBytes());
                    os.write(("这是返回给发送端的").getBytes());
                }
            }
        } catch (Exception e) {
        }
    }
}

class ClientThread extends Thread {
    Socket client;
    volatile boolean b = false;

    public ClientThread(Socket client) {
        this.client = client;
    }

    //读取服务器端的数据
    public void run() {
        try (BufferedReader br = new BufferedReader(
                new InputStreamReader(client.getInputStream()))) {
            while (!b) { //什么时候结束线程
                String msg = br.readLine();
                if (msg != null && !msg.equals("")) {
                    System.out.println(msg);
                }
            }
        } catch (Exception e) {
        }
    }
}